#pragma once

#include <iostream>

#include "bugfix/trace.h"

#include "container/array.h"
#include "container/vector.h"
#include "container/list.h"
#include "container/map.h"
#include "container/deque.h"
#include "container/queue.h"
#include "container/stack.h"
#include "container/priority.h"

#include "type/typerelation.h"
#include "type/typelist.h"
#include "type/typetraits.h"
#include "type/typeof.h"

#include "algorithm/foreach.h"
#include "algorithm/assign.h"

#include "utility/valid.h"
#include "utility/refer.h"
#include "utility/tuple.h"

//////////////////////////////////////////////////////////////////////////

class A
{
public:
    int a_;

public:
    A(void) : a_(0)
    { nx_out("cont A", nx::ent); }
    A(int a) : a_(a)
    { nx_out("cont A ", a_, nx::ent); }
    A(const A& a)
    { nx_out("copy A ", a_ = a.a_, nx::ent); }
    ~A(void)
    { nx_out("dest A ", a_, nx::ent); a_ = 0; }

public:
    template <typename T, size_t N>
    void operator=(const T(&a)[N])
    { a_ = a[0]; }
};

nx::tuple<int, char, double> func(void)
{
    int i = 1; char c = 2; double d = 3;
    return nx::tie(i, c, d);
}

void test_nx_trace()
{
    nx_out(nx::ent, "check nx_trace ->: ", nx::ent);
    nx_trace(true, " ", 123, nx::ent<2>);
    
    /////////////////////////////////////

    nx_out("check nx_auto ->: ", nx::ent);
    nx_auto(pcout, &(std::cout));
    nx_out("point size is ", sizeof(pcout), ", type is ", nx::format::check(pcout), nx::ent<2>);

    /////////////////////////////////////

    nx_out("check nx_check ->: ", nx::ent);
    bool b1 = true, b2 = true, b3 = false, b4 = true;
    nx_check(b1, b2, (b3 && b4), b4) {}

    /////////////////////////////////////

    nx_out("check nx_types ->: ");
    {
        typedef nx_types(char, void*, long, char*, void**, size_t*, int, long, long long) typelist_t;
        typedef nx_types_zip(typelist_t) tl_t;

        nx_types_at(tl_t, 2) a[nx_types_len(tl_t)];
        a[0] = (nx_types_at(tl_t, 2))nx_types_find(tl_t, long);
        nx_out(nx::count_of(a), " ", a[0], nx::ent);
    }
    nx_out(nx::ent);

    /////////////////////////////////////

    nx_out("check nx::pointer ->: ", nx::ent);
    {
        nx::pointer<void> p1(4), p2;
        p1 = p2;
        p2.set(nx::alloc(10), 10);
    }
    {
        nx_auto_pointer(p1, nx::alloc<A>());
        p1->a_ = 1;

        nx_auto_pointer(p2, nx::alloc<int[2]>());
        (*p2)[0] = 2;
        (*p2)[1] = 3;

        nx_out("pointer_cast", nx::ent);
        p1 = nx::pointer_cast<nx_typeof(p1)>(p2);

        nx_out("pa", nx::ent);
        nx_auto_pointer(pa, nx::alloc<A>());
        nx_out("pointer_cast", nx::ent);
        p1 = nx::pointer_cast<nx_typeof(p1)>(pa);
        nx_out("pa->a_ = ", pa->a_ = 3, nx::ent);
        nx_out("p1->a_ = ", p1->a_, nx::ent);
    }
    nx_out("end check nx::pointer", nx::ent<2>);

    /////////////////////////////////////

    nx_out("check nx_push, nx_foreach ->: ", nx::ent);
    {
        nx_auto(n, 2.0);
        nx_push(n, 1, 2, 3);
        nx_foreach(i, n)
        {
            nx_out(i, " ");
        }
        nx_out(nx::ent);

        int num_arr[8] = {0};
        nx::fill(num_arr, 3);
        nx_push(num_arr, 1, 2, 3, 4, 5, 6);
        nx_foreach(i, num_arr)
        {
            nx_out(*i, " ");
        }
        nx_out(nx::ent);

        long num_arr_l[8] = {0};
        nx::fill(num_arr_l, 4);
        nx::swap(num_arr_l, num_arr);   /* swap<long[], int[]> */
        nx_foreach(i, num_arr)
        {
            nx_out(*i, " ");
        }
        nx_out(nx::ent);

        nx::array<short, 8> static_arr;
        fill(static_arr, 4);            /* ADL(Argument Dependent Lookup) */
        nx_push(static_arr, 1, 2, 3, 4, 5, 6);
        nx_foreach(i, static_arr)
        {
            nx_out(*i, " ");
        }
        nx_out(nx::ent);

        nx_auto(& arr, nx::singleton<nx::vector<long> >());
        nx_push(nx::singleton<nx::vector<long> >(), 1, 2, 3, 4, 5, 6);
        nx_foreach(i, arr)
        {
            nx_out(*i, " ");
        }
        nx_out(nx::ent);

        nx::map<int, int> m;
        nx_push(m)(1, 2)(2, 3)(3, 4)(4, 5)(5, 6);
        nx_foreach(i, m)
        {
            nx_out("(", i->first, ", ", i->second, ") ");
        }
        nx_out(nx::ent);
    }
    nx_out(nx::ent);

    /////////////////////////////////////

    nx_out("check nx::valid ->: ", nx::ent);
    {
        A a(1);

        nx::valid<const A> vb = nx::type::none;
        nx::valid<const A&> va = vb/*nx::type::none*/;

        va = a;
        if (va || vb)
            nx_out("yes!", nx::ent);

        vb = a;
        va = *vb;
        va = vb;

        vb.reset();
        va.reset();
    }
    nx_out(nx::ent);

    /////////////////////////////////////

    nx_out("check nx::tuple, nx::tie ->: ", nx::ent);
    {
        int i = 0; char c = 0; double d = 0;
        nx_auto(tr, nx::tie(i, c, d));
        tr = func();
        nx::tuple<long, long, long> td(5, 5, 5);
        nx::swap(td, tr);
        nx_out(tr._<0>(), " ", tr._<1>(), " ", tr._<2>(), " ", nx::ent);
        nx_out(*td      , " ", td._<1>(), " ", td._<2>(), " ", nx::ent);
    }
    nx_out(nx::ent);
}
